//
//  DownGroffRenderable.swift
//  Down
//
//  Created by Rob Phillips on 5/31/16.
//  Copyright © 2016-2019 Down. All rights reserved.
//

import Foundation
import libcmark

public protocol DownGroffRenderable: DownRenderable {

    func toGroff(_ options: DownOptions, width: Int32) throws -> String

}

extension DownGroffRenderable {

    /// Generates a groff man string from the `markdownString` property.
    ///
    /// - Parameters:
    ///     - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
    ///     - width: The width to break on, defaulting to 0.
    ///
    /// - Returns:
    ///     A groff man string.
    ///
    /// - Throws:
    ///     `DownErrors` depending on the scenario.

    public func toGroff(_ options: DownOptions = .default, width: Int32 = 0) throws -> String {
        let ast = try DownASTRenderer.stringToAST(markdownString, options: options)
        let groff = try DownGroffRenderer.astToGroff(ast, options: options, width: width)
        cmark_node_free(ast)
        return groff
    }

}

public struct DownGroffRenderer {

    /// Generates a groff man string from the given abstract syntax tree.
    ///
    /// **Note:** caller is responsible for calling `cmark_node_free(ast)` after this returns.
    ///
    /// - Parameters:
    ///     - ast: The `cmark_node` representing the abstract syntax tree.
    ///     - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
    ///     - width: The width to break on, defaulting to 0.
    ///
    /// - Returns:
    ///     A groff man string.
    ///
    /// - Throws:
    ///     `ASTRenderingError` if the AST could not be converted.

    public static func astToGroff(_ ast: CMarkNode,
                                  options: DownOptions = .default,
                                  width: Int32 = 0) throws -> String {

        guard let cGroffString = cmark_render_man(ast, options.rawValue, width) else {
            throw DownErrors.astRenderingError
        }

        defer {
            free(cGroffString)
        }

        guard let groffString = String(cString: cGroffString, encoding: String.Encoding.utf8) else {
            throw DownErrors.astRenderingError
        }

        return groffString
    }

}
